package top.tenebrous.yzl.tree;

import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.SneakyThrows;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author yazhang6
 */
public class TreeUtils {

    /**
     * 构建树结构方法
     * 构建对象需要继承<p>top.tenebrous.yzl.tree.BaseTree</p>类，并将必要数据进行设置，才能更好的进行统一构建
     * 可以自定义设置父ID类型，在构建时进行指定（如果位基本数据类型，需要传入对应的包装类型）
     * @param baseValue         基础数据集合
     * @param defaultValue      默认父ID
     * @param <T>               真实数据类型
     * @param <E>               自定义类型
     * @return                  包装好的数据树结构
     */
    public static <T extends BaseTree<T, E>, E> List<T> buildTree(List<T> baseValue, E defaultValue) {
        List<T> result = new ArrayList<T>();
        if (CollectionUtils.isEmpty(baseValue)) {
            return result;
        }
        for (T value : baseValue) {
            if (defaultValue.equals(value.getParentId())) {
                result.add(value);
            }
            for (T t : baseValue) {
                if (t.getParentId().equals(value.getId())) {
                    if (CollectionUtils.isEmpty(value.getChildren())) {
                        List<T> myChildrens = new ArrayList<>();
                        myChildrens.add(t);
                        value.setChildren(myChildrens);
                    } else {
                        value.getChildren().add(t);
                    }
                }
            }
        }
        return result;
    }


    private static <T extends BaseTree<T, E>, E> void buildChild(List<T> baseValue, T value, List<T> result, CountDownLatch countDownLatch) {
        System.out.println("----构建:[" + value.getId() + "]子节点开始");
        for (T t : baseValue) {
            if (t.getParentId().equals(value.getId())) {
                if (CollectionUtils.isEmpty(value.getChildren())) {
                    List<T> myChildrens = new ArrayList<>();
                    myChildrens.add(t);
                    value.setChildren(myChildrens);
                } else {
                    value.getChildren().add(t);
                }
            }
        }
        System.out.println("----构建:[" + value.getId() + "]子节点结束");
        countDownLatch.countDown();
    }


    /**
     * 快速构建树结构方法
     * 对每个子节点构建进行多线程操作
     * 构建对象需要继承<p>top.tenebrous.yzl.tree.BaseTree</p>类，并将必要数据进行设置，才能更好的进行统一构建
     * 可以自定义设置父ID类型，在构建时进行指定（如果位基本数据类型，需要传入对应的包装类型）
     * @param baseValue         基础数据集合
     * @param defaultValue      默认父ID
     * @param <T>               真实数据类型
     * @param <E>               自定义类型
     * @return                  包装好的数据树结构
     */
    @SneakyThrows
    public static <T extends BaseTree<T, E>, E> List<T> buildTreeAny(List<T> baseValue, E defaultValue, Class<?> excutor) {
        List<T> result = new ArrayList<T>();
        if (CollectionUtils.isEmpty(baseValue)) {
            return result;
        }
        int i = (baseValue.size() / baseValue.size()> 2000 ? 1000 : 100) + 1;

        // 拆分大list为多个小list
        List<List<T>> lists = splitBigListIntoSmallLists(baseValue, i);
        CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        for (List<T> list : lists) {
            new Thread() {
                @Override
                public void run() {
                    buildParent(list, result,defaultValue, countDownLatch);
                }
            }.start();
        }
        countDownLatch.await();
        return result;
    }

    @SneakyThrows
    private static <T extends BaseTree<T, E>, E> void buildParent(List<T> baseValue, List<T> result, E defaultValue, CountDownLatch countDownLatch) {
        // 获取父ID个数
        long count = baseValue.stream().filter(item -> defaultValue.equals(item.getParentId())).count();
        CountDownLatch latch = new CountDownLatch((int) count);
        for (T value : baseValue) {
            System.out.println("构建:[" + value.getId() + "]开始");
            if (defaultValue.equals(value.getParentId())) {
                result.add(value);
            }
            new Thread(){
                @Override
                public void run() {
                    buildChild(baseValue, value, result, latch);
                }
            }.start();
            System.out.println("构建:[" + value.getId() + "]结束");
        }
        latch.await();
        countDownLatch.countDown();
    }

    public static <T> List<List<T>> splitBigListIntoSmallLists(List<T> bigList, int subListSize) {
        List<List<T>> smallLists = new ArrayList<>();
        int totalSize = bigList.size();
        int numberOfSubLists = (totalSize + subListSize - 1) / subListSize;

        for (int i = 0; i < numberOfSubLists; i++) {
            int startIndex = i * subListSize;
            int endIndex = Math.min(startIndex + subListSize, totalSize);
            List<T> smallList = bigList.subList(startIndex, endIndex);
            smallLists.add(smallList);
        }

        return smallLists;
    }

    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        List<Teacher> teachers = queryTeacherList();
        System.out.println("构建参数完成.......");
        long startTime = System.currentTimeMillis();
        List<Teacher> tree = buildTreeAny(teachers, "0", null);
//        List<Teacher> tree = buildTree(teachers, "0");
        long endTime = System.currentTimeMillis();
//        System.out.println("多线程打印:\n"+JSON.toJSONString(tree, true));

        long startTime1 = System.currentTimeMillis();
//        List<Teacher> tree1 = buildTreeAny(teachers, "0", null);
        List<Teacher> tree1 = buildTree(teachers, "0");
//        System.out.println("单线程打印:\n"+JSON.toJSONString(tree1, true));
        long endTime2 = System.currentTimeMillis();
        System.out.println("单线程耗时:【" + (endTime2 - startTime1) + "】ms");
        System.out.println("多线程耗时:【" + (endTime - startTime) + "】ms");
    }

    @SneakyThrows
    private static List<Teacher> queryTeacherList() {
        List<Teacher> result = new ArrayList<>();
        int level1 = 10000;
        int level2 = 5;
        int level3 = 5;
        CountDownLatch countDownLatch = new CountDownLatch(level1);
        CountDownLatch countDownLatch1 = new CountDownLatch(level2);
        CountDownLatch countDownLatch2 = new CountDownLatch(level3);
        for (int i = 1; i <= level1; i++) {
            Teacher teacher = new Teacher();
            teacher.setName("老师:" + i);
            teacher.setLevel(String.valueOf(i));
            teacher.setParentId("0");
            teacher.setId(String.valueOf(i));

            List<Teacher> children = new ArrayList<>();
            for (int j = 0; j < level2; j++) {
                Teacher teacherChild = new Teacher();
                teacherChild.setName("老师:" + i + ":" + j);
                teacherChild.setLevel(String.valueOf(i) + String.valueOf(j));
                teacherChild.setParentId(String.valueOf(i));
                teacherChild.setId(String.valueOf(i) + String.valueOf(j));
                children.add(teacherChild);

                List<Teacher> children2 = new ArrayList<>();

                for (int k = 0; k < level3; k++) {
                    Teacher teacherChild2 = new Teacher();
                    teacherChild2.setName("老师:" + i + ":" + j + ":" + k);
                    teacherChild2.setLevel(String.valueOf(i) + String.valueOf(j) + String.valueOf(k));
                    teacherChild2.setParentId(String.valueOf(i));
                    teacherChild2.setId(String.valueOf(i) + String.valueOf(j) + String.valueOf(k));
                    children2.add(teacherChild2);
                    countDownLatch2.countDown();
                }
                teacherChild.setChildren(children2);
                countDownLatch1.countDown();
            }
            teacher.setChildren(children);
            result.add(teacher);
            countDownLatch.countDown();
        }
        countDownLatch2.await();
        countDownLatch1.await();
        countDownLatch.await();
        return result;
    }
}

@Data
class Teacher extends BaseTree<Teacher, String> {

    private String name;

    private String level;

}